package org.lc.platform.mybatis.manager.query;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;
import lombok.Getter;
import org.lc.platform.mybatis.manager.annotation.OrderBy;
import org.lc.platform.mybatis.manager.enums.RelationEnum;
import org.lc.platform.mybatis.manager.interfaces.AbstractQueryBuilder;
import org.lc.platform.mybatis.manager.relation.Relation;
import org.lc.platform.mybatis.manager.relation.RelationBuilder;
import org.lc.platform.mybatis.manager.relation.TreeRelationBuilder;

import java.lang.reflect.Field;
import java.util.*;


public class QueryBuilder<T> extends AbstractChainWrapper<T, String, QueryBuilder<T>, QueryWrapper<T>> implements AbstractQueryBuilder<T, QueryBuilder<T>> {

    private Map<Object, Relation> relationMap = new HashMap<>();

    private List<Map<String, Object>> relationList = new ArrayList<>();

    private Map<String, Boolean> orderByMap = null;

    @Getter
    private final Class<T> entityClass;

    public QueryBuilder(Class<T> entityClass) {
        this.entityClass = entityClass;
        super.wrapperChildren = new QueryWrapper<>();
        this.initOrderByMap();
        this.initRelationMap();

    }

    public QueryBuilder(Class<T> entityClass, List<Map<String, Object>> relationList) {
        this(entityClass);
        relationList.forEach(r -> {
            setTableList((String) r.get("table"));
        });
    }

    private void initOrderByMap() {
        OrderBy ob = this.entityClass.getAnnotation(OrderBy.class);
        if (ob != null && ob.params().length > 0) {
            orderByMap = new HashMap<>();
            Arrays.stream(ob.params()).forEach(o -> {
                orderByMap.put(o.field(), o.isAsc());
            });
        }
    }

    private void initRelationMap() {
        Field[] fields = ReflectUtil.getFields(this.getEntityClass());
        for (Field f : fields) {
            Relation rel = Relation.of(f);
            if (rel.isHasRelation()) {
                relationMap.put(rel.getRelationName(), rel);
                if (rel.isEager()) {
                    setTableList(rel.getRelationName());
                }
            }
        }
    }

    private void setOrderBy() {
        if (orderByMap != null) {
            orderByMap.forEach((k, v) -> {
                this.orderBy(true, v, StrUtil.toUnderlineCase(k));
            });
        }
    }

    private boolean exitsRelation() {
        return !relationMap.isEmpty() && !relationList.isEmpty();
    }

    private Relation getRelation(Object key) {
        return relationMap.get(key);
    }

    public void setTableList(String tableName) {
        boolean exits = relationList.stream().anyMatch(r -> tableName.equals(r.get("table")));
        if (!exits) {
            boolean valid = StrUtil.isNotEmpty(tableName) && Optional.ofNullable(relationMap.get(tableName)).isPresent();
            if (valid) {
                Map<String, Object> map = new HashMap<>();
                map.put("table", tableName);
                relationList.add(map);
            }
        }
    }

    @Override
    public QueryBuilder<T> relation(String tableName) {
        if (tableName != null) {
            setTableList(tableName);
        }
        return this;
    }

    @Override
    public QueryBuilder<T> relation(Class<?> cls) {
        TableName tn = cls.getAnnotation(TableName.class);
        setTableList(StrUtil.isNotEmpty(tn.value()) ? tn.value() : StrUtil.toUnderlineCase(cls.getSimpleName()));
        return this;
    }


    @Override
    public T one() {
        T data = AbstractQueryBuilder.super.one();
        if (exitsRelation() && data != null) {
            relationList.forEach(r -> querySingleRelations(getRelation(r.get("table")), data));
        }
        return data;
    }

    @Override
    public List<T> list() {
        /* 先默认排序 */
        setOrderBy();
        List<T> data = AbstractQueryBuilder.super.list();
        if (exitsRelation() && data != null && !data.isEmpty()) {
            relationList.forEach(r -> queryMultipleRelations(getRelation(r.get("table")), data));
        }
        return data;
    }

    @Override
    public <E extends IPage<T>> E page(E page) {
        var pages = AbstractQueryBuilder.super.page(page);
        var data = pages.getRecords();
        if (exitsRelation() && data != null && !data.isEmpty()) {
            relationList.forEach(r -> queryMultipleRelations(getRelation(r.get("table")), data));
        }
        return pages;
    }


    public List<T> selectTreeLevel() {
        List<T> data = this.list();
        var builder = new TreeRelationBuilder<T>(this.getEntityClass(), data, this.relationList);
        builder.selectTree();
        return data;
    }

    public void querySingleRelations(Relation rel, T data) {
        queryMultipleRelations(rel, List.of(data));
    }

    public void queryMultipleRelations(Relation rel, List<T> data) {
        /* 关联方式 */
        RelationEnum type = rel.getRelationType();
        RelationBuilder<T> builder = RelationBuilder.of(rel, data);
        switch (type) {
            case MANY_TO_ONE, ONE_TO_ONE -> builder.processToOne();
            case ONE_TO_MANY -> builder.processToMany();
            case MANY_TO_MANY -> builder.processManyToMany();
        }
    }
}